﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

namespace Wicresoft.Shell.Package.AppExp
{
    /// <summary>
    /// Built-in FileSystemWatcher has a side effect. When we watch file changed event,
    /// the watcher will trigger the event handler multiple times. Since we want to write
    /// data back to database in event handler, this side effect may cost us more times.
    /// This derived watcher resolves this issue.
    /// </summary>
    class UmcFileSystemWatcher : FileSystemWatcher
    {
        /// <summary>
        /// Only support changed event for now. Update this class to support more events.
        /// </summary>
        /// <param name="changedEvent"></param>
        public UmcFileSystemWatcher(FileSystemEventHandler changedEvent)
            : base()
        {
            timer = new Timer(new TimerCallback(timer_Callback),
                null,
                Timeout.Infinite,
                Timeout.Infinite);

            eventHandler = changedEvent;
            base.Changed += new FileSystemEventHandler(UmcFileSystemWatcher_Changed);
        }

        void UmcFileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            Mutex mutex = new Mutex(false, UmcFileSystemWatcher.mutextName);
            mutex.WaitOne();
            // We use a list to store all the files need to be handled.
            // Only add the file once to avoid handling it multiple times.
            if (!fileList.Contains(e.FullPath))
            {
                fileList.Add(e.FullPath);
            }
            mutex.ReleaseMutex();

            // Trigger timer callback once
            timer.Change(UmcFileSystemWatcher.timeoutMillis, Timeout.Infinite);
        }

        void timer_Callback(object state)
        {
            List<string> filesToHandle = new List<string>();

            Mutex mutex = new Mutex(false, UmcFileSystemWatcher.mutextName);
            mutex.WaitOne();
            filesToHandle.AddRange(fileList);
            fileList.Clear();
            mutex.ReleaseMutex();

            foreach (string file in filesToHandle)
            {
                eventHandler(this, new FileSystemEventArgs(WatcherChangeTypes.Changed,
                    System.IO.Path.GetDirectoryName(file),
                    System.IO.Path.GetFileName(file)));
            }
        }

        private FileSystemEventHandler eventHandler;
        private Timer timer;
        private static readonly int timeoutMillis = 2000; 
        private static readonly string mutextName = "UmcFSW";
        private List<string> fileList = new List<string>();
    }
}
